﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Text;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using SFML.Window;
using Theta;
using Theta.Animations;
using Theta.Serialization;

namespace Animation_Editor
{
    public partial class MainForm : Form
    {
        public BindingList<Frame> Frames = new BindingList<Frame>();

        public bool TimerRunning = false;
        public int TimerCurIndex = 0;
        public Image[] PreviewImages;
        public Font VisitorFont;

        public Vector2i FrameSize
        {
            get
            {
                Vector2i pos;
                string[] strings = textBox1.Text.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
                pos.X = int.Parse(strings[0]);
                pos.Y = int.Parse(strings[1]);
                return pos;
            }
        }

        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            framesListBox.DataSource = Frames;
            outlineButton.BackColor = outlineColorDialog.Color;
            textButton.BackColor = textColorDialog.Color;
            
            PrivateFontCollection p = new PrivateFontCollection();
            p.AddFontFile("visitor1.ttf");
            VisitorFont = new Font(p.Families[0], 12f, FontStyle.Regular);
        }

        private void addButton_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(posTextBox.Text) || string.IsNullOrWhiteSpace(lifetimeTextBox.Text))
                return;

            Frames.Add(GetFrameData());
            pictureBox1.Refresh();
        }

        private double GetLifetime()
        {
            return double.Parse(lifetimeTextBox.Text);
        }

        private Vector2i GetPosition()
        {
            Vector2i pos;

            string[] strings = posTextBox.Text.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
            pos.X = int.Parse(strings[0]);
            pos.Y = int.Parse(strings[1]);

            return pos;
        }

        private Frame GetFrameData()
        {
            return new Frame(GetPosition(), GetLifetime());
        }
        
        private void posTextBox_Validating(object sender, CancelEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(posTextBox.Text))
                return;
            //Pattern: One or more number + zero or more whitespaces + Comma + zero or more whitespaces + One or more number
            if(Regex.IsMatch(posTextBox.Text, @"\d+\s*\,\s*\d+"))
            {
                string s = posTextBox.Text;
                s = s.Replace(" ", "");
                s = s.Replace(",", ", ");
                posTextBox.Text = s;
                posErrorLabel.Text = "";
            }
            else
            {
                posErrorLabel.Text = "Format like this: 'X, Y'";
            }
        }

        private void lifetimeTextBox_Validating(object sender, CancelEventArgs e)
        {
            double value;
            if (string.IsNullOrWhiteSpace(lifetimeTextBox.Text))
                return;
            if(!double.TryParse(lifetimeTextBox.Text, out value))
            {
                lifetimeErrorLabel.Text = "Input only numbers";   
            }
            else
            {
                lifetimeErrorLabel.Text = "";
            }
        }

        private void removeButton_Click(object sender, EventArgs e)
        {
            if(framesListBox.SelectedItem != null)
                Frames.Remove((Frame)framesListBox.SelectedItem);
            pictureBox1.Refresh();
        }

        private void browseButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = @"Image Files|*.jpg;*.jpeg;*.png;*.bmp|All Files(*.*)|*.*";

            if(ofd.ShowDialog() == DialogResult.OK)
            {
                previewPictureBox.Image = null;
                pathTextBox.Text = ofd.FileName;
                System.Drawing.Image i = System.Drawing.Image.FromFile(pathTextBox.Text);

                if(i.Size.Width > pictureBox1.Size.Width || i.Size.Height > pictureBox1.Size.Height)
                {
                    pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
                    hScrollBar1.Enabled = true;
                    vScrollBar1.Enabled = true;
                    vScrollBar1.Maximum = i.Height - panel1.Height;
                    hScrollBar1.Maximum = i.Width - panel1.Width;
                }
                else
                {
                    pictureBox1.SizeMode = PictureBoxSizeMode.Normal;
                    hScrollBar1.Enabled = false;
                    vScrollBar1.Enabled = false;
                    hScrollBar1.Maximum = 0;
                    vScrollBar1.Maximum = 0;
                }
                pictureBox1.ImageLocation = pathTextBox.Text;

                groupBox1.Enabled = true;
                groupBox3.Enabled = true;
            }
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            if (framesListBox.Items.Count <= 0)
                return;

            Pen p = new Pen(outlineColorDialog.Color, 2);
            for(int i = 0; i < Frames.Count; i++)
            {
                Frame frame = Frames[i];
                e.Graphics.DrawString(i.ToString(), VisitorFont, new SolidBrush(textColorDialog.Color), frame.SheetPosition.X, frame.SheetPosition.Y);
                e.Graphics.DrawRectangle(p, frame.SheetPosition.X, frame.SheetPosition.Y, FrameSize.X, FrameSize.Y);
            }
        }

        private void textBox1_Validating(object sender, CancelEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(posTextBox.Text))
                return;
            //Pattern: One or more number + zero or more whitespaces + Comma + zero or more whitespaces + One or more number
            if (Regex.IsMatch(textBox1.Text, @"\d+\s*\,\s*\d+"))
            {
                string s = textBox1.Text;
                s = s.Replace(" ", "");
                s = s.Replace(",", ", ");
                textBox1.Text = s;
                sizeErrorLabel.Text = "";
            }
            else
            {
                sizeErrorLabel.Text = "Format like this: 'X, Y'";
            }
        }

        private void framesListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (framesListBox.SelectedIndex >= 0)
            {
                Frame f = Frames[framesListBox.SelectedIndex];
                StringBuilder sb = new StringBuilder();
                sb.Append(f.SheetPosition.X);
                sb.Append(", ");
                sb.Append(f.SheetPosition.Y);
                posTextBox.Text = sb.ToString();
                lifetimeTextBox.Text = f.Lifetime.ToString();
                if(FrameSize.X > previewPictureBox.Size.Width || FrameSize.Y > previewPictureBox.Size.Height)
                    previewPictureBox.SizeMode = PictureBoxSizeMode.Zoom;
                else
                    previewPictureBox.SizeMode = PictureBoxSizeMode.Normal;
                
                Image croped = Utils.CropDotnetImage(Image.FromFile(pathTextBox.Text),
                                                                new Rectangle(f.SheetPosition.X, f.SheetPosition.Y,
                                                                              FrameSize.X, FrameSize.Y));
                if(croped == null)
                {
                    MessageBox.Show("Either Frame Size is greater than the spritesheet size\nor the frame position is out of bounds, preview is unavailble.", "Out of bounds");
                    return;
                }

                previewPictureBox.Image = croped;
            }
            pictureBox1.Refresh();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (framesListBox.SelectedIndex < 0)
                return;

            if (lifetimeErrorLabel.Text != "" || posErrorLabel.Text != "")
                return;

            Frames[framesListBox.SelectedIndex] = GetFrameData();
        }

        private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)
        {
            this.pictureBox1.Left = -this.hScrollBar1.Value;
        }

        private void vScrollBar1_Scroll(object sender, ScrollEventArgs e)
        {
            this.pictureBox1.Top = -this.vScrollBar1.Value;
        }

        private void outlineButton_Click(object sender, EventArgs e)
        {
            outlineColorDialog.ShowDialog();
            outlineButton.BackColor = outlineColorDialog.Color;
            pictureBox1.Refresh();
        }

        private void textButton_Click(object sender, EventArgs e)
        {
            textColorDialog.ShowDialog();
            textButton.BackColor = textColorDialog.Color;
            pictureBox1.Refresh();
        }

        private void button2_Click_1(object sender, EventArgs e)
        {
            if (framesListBox.Items.Count <= 0)
                return;

            int idx = TimerCurIndex;

            if (framesListBox.SelectedIndex >= 0)
                idx = framesListBox.SelectedIndex;

            PreviewImages = new Image[Frames.Count];

            for(int i = 0; i < PreviewImages.Length; i++)
            {
                PreviewImages[i] = Utils.CropDotnetImage(Image.FromFile(pathTextBox.Text),
                                                                new Rectangle(Frames[i].SheetPosition.X, Frames[i].SheetPosition.Y,
                                                                              FrameSize.X, FrameSize.Y));
            }
            timer1.Interval = (int)(Frames[idx].Lifetime * 1000);
            timer1.Start();
            TimerRunning = true;

            groupBox1.Enabled = false;
            textBox1.Enabled = false;
        }

        private void button4_Click(object sender, EventArgs e)
        {
            if (!TimerRunning)
                return;
            timer1.Stop();
            previewPictureBox.Image = Utils.CropDotnetImage(Image.FromFile(pathTextBox.Text),
                                                                new Rectangle(Frames[0].SheetPosition.X, Frames[0].SheetPosition.Y,
                                                                              FrameSize.X, FrameSize.Y));
            TimerCurIndex = 0;

            for(int i = 0; i < PreviewImages.Length; i++)
            {
                PreviewImages[i].Dispose();
            }

            PreviewImages = null;

            groupBox1.Enabled = true;
            textBox1.Enabled = true;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if(TimerCurIndex >= Frames.Count - 1)
                TimerCurIndex = 0;
            else
                TimerCurIndex++;

            previewPictureBox.Image = PreviewImages[TimerCurIndex];
        }

        private void button3_Click(object sender, EventArgs e)
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.AddExtension = true;
            sfd.Filter = @"Theta Animation File|*.taf|All Files(*.*)|*.*";

            if (sfd.ShowDialog() == DialogResult.OK)
            {
                if(File.Exists(sfd.FileName))
                    File.Delete(sfd.FileName);

                FileStream f = new FileStream(sfd.FileName, FileMode.Create, FileAccess.Write);
                int method = -1;

                switch (methodComboBox.SelectedText)
                {
                    case "Json":
                        f.WriteByte(0);
                        method = 0;
                        break;
                    case "BinaryFormatter":
                        f.WriteByte(1);
                        method = 1;
                        break;
                }

                if (method == 0)
                {
                    JsonSerializer.Serialize(f, new Animation(Frames.ToArray(), FrameSize, checkBox1.Checked));
                }
                else if (method == 1)
                {
                    BinarySerializer.Serialize(f, new Animation(Frames.ToArray(), FrameSize, checkBox1.Checked));
                }

                f.Flush();
                f.Close();
            }
        }
    }
}
